package cn.net.yato.picture.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.net.yato.picture.biz.PermissionBiz;
import cn.net.yato.picture.biz.SysBiz;
import cn.net.yato.picture.constant.CommParams;
import cn.net.yato.picture.entity.Permission;
import com.mysql.cj.util.StringUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName ShiroConfig
 * @Description shiro权限管理配置类
 * @Author yato
 * @Date 2020/01/11 08:40
 * @Version 1.0
 */
@Configuration
public class ShiroConfig {

	@Resource
	private SysBiz sysBiz;
	@Resource
	private PermissionBiz permissionBiz;

	@Value("${spring.redis.host}")
	private String host;
	@Value("${spring.redis.timeout}")
	private int timeout;

	@Bean(name = "shiroDialect")
	public ShiroDialect shiroDialect(){
		return new ShiroDialect();
	}

	/**
	 * 配置shiro redisManager
	 * 使用的是shiro-redis开源插件
	 * @return
	 */
	public RedisManager redisManager() {
		RedisManager redisManager = new RedisManager();
		redisManager.setHost(host);
		redisManager.setTimeout(timeout);
		return redisManager;
	}

	/**
	 * cacheManager 缓存 redis实现
	 * 使用的是shiro-redis开源插件
	 * @return
	 */
	public RedisCacheManager cacheManager() {
		RedisCacheManager redisCacheManager = new RedisCacheManager();
		redisCacheManager.setRedisManager(redisManager());
		redisCacheManager.setPrincipalIdFieldName("id");
		redisCacheManager.setExpire(1800);
		return redisCacheManager;
	}

	/**
	 * RedisSessionDAO shiro sessionDao层的实现 通过redis
	 * 使用的是shiro-redis开源插件
	 */
	@Bean
	public RedisSessionDAO redisSessionDAO() {
		RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
		redisSessionDAO.setRedisManager(redisManager());
		return redisSessionDAO;
	}

	/**
	 * Session Manager
	 * 使用的是shiro-redis开源插件
	 */
	@Bean
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setSessionDAO(redisSessionDAO());
		return sessionManager;
	}

	@Bean
	public UserNamePasswordRealm userNamePasswordRealm(){
		UserNamePasswordRealm userNamePasswordRealm = new UserNamePasswordRealm();
		userNamePasswordRealm.setCachingEnabled(true);
		userNamePasswordRealm.setAuthorizationCachingEnabled(true);
		userNamePasswordRealm.setAuthorizationCacheName("authorizationCacheUserNamePassword");
		return userNamePasswordRealm;
	}


	@Bean
	public EmailAndVerificationCodeShiroRealm emailAndVerificationCodeShiroRealm(){
		EmailAndVerificationCodeShiroRealm emailAndVerificationCodeShiroRealm = new EmailAndVerificationCodeShiroRealm();
		emailAndVerificationCodeShiroRealm.setCachingEnabled(true);
		emailAndVerificationCodeShiroRealm.setAuthorizationCachingEnabled(true);
		emailAndVerificationCodeShiroRealm.setAuthorizationCacheName("authorizationCacheEmailCode");
		return emailAndVerificationCodeShiroRealm;
	}

	@Bean
	public PhoneAndVerificationCodeShiroRealm phoneAndVerificationCodeShiroRealm(){
		PhoneAndVerificationCodeShiroRealm phoneAndVerificationCodeShiroRealm = new PhoneAndVerificationCodeShiroRealm();
		phoneAndVerificationCodeShiroRealm.setCachingEnabled(true);
		phoneAndVerificationCodeShiroRealm.setAuthorizationCachingEnabled(true);
		phoneAndVerificationCodeShiroRealm.setAuthorizationCacheName("authorizationCachePhoneCode");
		return phoneAndVerificationCodeShiroRealm;
	}

	@Bean
	public WeCatOpenIdRealm weCatOpenIdRealm(){
		WeCatOpenIdRealm weCatOpenIdRealm = new WeCatOpenIdRealm();
		weCatOpenIdRealm.setCachingEnabled(true);
		weCatOpenIdRealm.setAuthorizationCachingEnabled(true);
		weCatOpenIdRealm.setAuthorizationCacheName("authorizationCachePhoneCode");
		return weCatOpenIdRealm;
	}

	@Bean
	public MultiRealmAuthenticator multiRealmAuthenticator(){
		MultiRealmAuthenticator multiRealmAuthenticator = new MultiRealmAuthenticator();
		multiRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
		return multiRealmAuthenticator;
	}

	@Bean
	public DefaultWebSecurityManager securityManager(){
		DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
		List<Realm> realms = new ArrayList<>();
		/*用户名密码登录认证*/
		realms.add(userNamePasswordRealm());
		/*短信登录认证*/
		realms.add(phoneAndVerificationCodeShiroRealm());
		/*邮箱登录认证*/
		realms.add(emailAndVerificationCodeShiroRealm());
		/*微信扫码登录*/
		realms.add(weCatOpenIdRealm());
		securityManager.setAuthenticator(multiRealmAuthenticator());
		securityManager.setRealms(realms);
		securityManager.setRememberMeManager(rememberMeManager());
		securityManager.setCacheManager(cacheManager());
		securityManager.setSessionManager(sessionManager());
		return securityManager;
	}

	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean() {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager());
		shiroFilterFactoryBean.setLoginUrl("/login.html");
		shiroFilterFactoryBean.setSuccessUrl("/backstage/home.html");
		shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");
		/**
		 * 拦截器
		 * authc：所有的都拦截
		 * anon：可匿名访问
		 */
		Map<String, String> map = new LinkedHashMap<>();
		/*静态资源*/
		map.put("/css/**", "anon");
		map.put("/js/**", "anon");
		map.put("/fonts/**", "anon");
		map.put("/images/**", "anon");
		map.put("/layuiadmin/**", "anon");

		/*错误页面*/
		map.put("/403.html", "anon");
		map.put("/404.html", "anon");
		map.put("/500.html", "anon");

		/*主页*/
		map.put("/login.html", "anon");

		/*用户名登录认证接口*/
		map.put("/doLogin.json", "anon");

		/*手机短信登录认证接口*/
		map.put("/doPhoneLogin.json", "anon");

		/*邮箱认证登录接口*/
		map.put("/doEmailLogin.json", "anon");

		/*发送短信接口*/
		map.put("/sendSMS.json", "anon");

		/*发送邮件接口*/
		map.put("/sendEmail.json", "anon");

		/*微信回调接口*/
		map.put("/weCatLoginCallback", "anon");

		/*微信扫码接口*/
		map.put("/weCatLogin.html", "anon");

		/*微信认证接口*/
		map.put("/doWeCatLogin.json", "anon");

		map.put("/sys/header.html", "anon");
		/*退出登录接口*/
		map.put("/logout", "anon");
		int i = 1;
		//循环所有权限
		List<Permission> permissions = permissionBiz.getAllPermissionsBiz();
		for (Permission permission : permissions) {
			if (StringUtils.isNullOrEmpty(permission.getUrl())||permission.getUrl()==""){
				continue;
			}
			map.put(permission.getUrl(), "perms["+permission.getPerms()+"]");
		}
		map.put("/**", "user");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
		return shiroFilterFactoryBean;
	}

	/**
	 * cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe，自定义
	 * @return
	 */
	@Bean
	public SimpleCookie rememberMeCookie(){
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setPath("/");
		simpleCookie.setMaxAge(1800);
		return simpleCookie;
	}

	/**
	 * cookie管理对象;记住我功能,rememberMe管理器
	 * @return
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager(){
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
		return cookieRememberMeManager;
	}

	/**
	 * FormAuthenticationFilter 过滤器 过滤记住我
	 * @return
	 */
	@Bean
	public FormAuthenticationFilter formAuthenticationFilter(){
		FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
		formAuthenticationFilter.setRememberMeParam("rememberMe");
		return formAuthenticationFilter;
	}

	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");
		hashedCredentialsMatcher.setHashIterations(CommParams.USER_PWD_HASHITERATIONS);
		return hashedCredentialsMatcher;
	}

}